Previous slide Next slide Toggle fullscreen Open presenter view
업무 모듈 디렉토리 깊게 파헤치기
Areas·Controllers·Pages·Contracts의 역할을 구분하고, 실행 중인 모듈 설정을 찾습니다.
ASP.NET Core 공유 프레임워크 사용 (1/2)
.NET Core 3.0 이후 클래스 라이브러리에서 ASP.NET Core API 사용하도록 개선되었습니다.
Microsoft.NET.Sdk 또는 Microsoft.NET.Sdk.Razor SDK를 사용하는 프로젝트는 공유 프레임워크에서 ASP.NET Core API를 사용하려면 ASP.NET Core를 참조합니다.
MVC 확장성 포함 Razor 보기 또는 Razor 페이지 지원이 가능합니다.
ASP.NET Core 공유 프레임워크 사용 (2/2)
<Project Sdk ="Microsoft.NET.Sdk.Razor" >
<ItemGroup >
<FrameworkReference Include ="Microsoft.AspNetCore.App" />
</ItemGroup >
</Project >
Area란 무엇인가? (1/2)
ASP.NET Core 에서 컨트롤러, 뷰, 모델을 논리적으로 구분해 영역별로 모듈화하기 위한 기능입니다.
Area를 경로에 포함해 모듈을 구분합니다. 실제 Route 조합의 충돌 여부는 확인해야 합니다.
/{area}/{controller}/{action}/{id?}
Area란 무엇인가? (2/2)
/Areas
/Admin
/Controllers
DashboardController.cs
/Views
/Dashboard
Index.cshtml
/Models
API의 심장: Controllers 디렉토리 (1/2)
위치: Areas/[모듈 ID]/Controllers
역할: 클라이언트(웹 브라우저 등)로부터 오는 API 요청을 받아 처리 하는 로직이 담겨있습니다.
파일: HomeController.cs, BoardController.cs 와 같은 C# 클래스 파일로 구성됩니다.
주로 JSON 형태의 데이터를 주고받는 RESTful API 엔드포인트를 정의합니다.
API의 심장: Controllers 디렉토리 (2/2)
/Areas
└── sample
└── Controllers
└── HomeController.cs
Controllers 예시 코드 (2/3)
using Microsoft.AspNetCore.Mvc;
namespace sample.Areas.sample.Controllers
{
[Area("sample" ) ]
[Route("[area]/api/[controller]" ) ]
public class HomeController : Controller
{
[HttpGet("hello" ) ]
public ActionResult<string > SayHello ()
{
return "Hello from Server!" ;
}
}
Controllers 예시 코드 (3/3)
}
서버가 그리는 화면: Pages 디렉토리 (1/2)
위치: Areas/[모듈 ID]/Pages
역할: 서버 측에서 렌더링 되는 UI, 즉 사용자에게 보여질 화면(HTML)을 만듭니다.
파일: 주로 Razor Pages 기술을 사용하며, Index.cshtml(View)과 Index.cshtml.cs(Code-behind) 파일이 하나의 쌍으로 동작합니다.
서버의 데이터를 받아 동적인 웹 페이지를 생성할 때 사용됩니다.
서버가 그리는 화면: Pages 디렉토리 (2/2)
/Areas
└── Sample
└── Pages
├── Index.cshtml
└── Index.cshtml.cs
계약의 정의: Contracts 디렉토리 (1/2)
위치: [모듈 ID]/Contracts 화면/기능 개발자의 메인 디렉토리입니다.
컴파일 할 때 모든 모듈의 환경 설정과 개발 소스는 프론트엔드와 백엔드 모두 $(HANDSTACK_HOME)/contracts 단일 디렉토리내에서 관리됩니다.
화면·쿼리 계약은 텍스트로 편집합니다. 반영 경로와 캐시 정책을 확인하며, 서버 C# 변경은 별도 빌드가 필요합니다.
계약의 정의: Contracts 디렉토리 (2/2)
[모듈 ID]/contracts
├─command
├─dbclient
├─function
├─graphclient
├─prompter
├─repository
├─transact
└─wwwroot
contracts 디렉토리 구조
프로젝트 관리에 필요한 소스 운영 기준을 만들기 위해 디렉토리와 파일 명명에 대한 규칙을 다음과 같이 권장합니다.
module
└─HDS: 기본 애플리케이션 ID 3자리
└─TST: 프로젝트 ID 3자리
└─TST010: 파일 ID
어플리케이션 계약 디렉토리 (1/4)
HandStack에서 공식 제공되는 모든 디렉토리와 소스 코드 들은 이 규칙을 준수합니다.
어플리케이션 계약 디렉토리 (2/4)
%HANDSTACK_HOME%\contracts
├─command
│ └─HDS
│ └─TST
├─dbclient
│ └─HDS
│ └─TST
├─function
│ ├─csharp
│ │ └─HDS
│ │ └─TST
│ │ └─TST010
│ └─javascript
│ └─HDS
어플리케이션 계약 디렉토리 (3/4)
│ └─TST
│ └─TST020
├─graphclient
│ └─HDS
│ └─TST
├─prompter
│ └─HDS
│ └─TST
├─repository
│ └─HDS
├─transact
│ └─HDS
│ ├─STR
│ └─TST
어플리케이션 계약 디렉토리 (4/4)
└─wwwroot
└─HDS
└─TST
그외에 모듈 내 디렉토리 구조
모듈의 가독성과 유지보수성을 위해 다음과 같은 디렉토리 구조를 권장합니다.
Entity: 데이터베이스 테이블과 매핑되는 모델 클래스
Events: 비동기 처리를 위한 이벤트 정의
Extensions: 기존 클래스를 확장하는 헬퍼 메서드
Services: 비즈니스 로직을 처리하는 서비스 클래스
Settings: 모듈별 환경 설정 값
HandStack의 ack 서버
ack 서버는 HandStack 애플리케이션의 시작점입니다.
이 웹 서버가 시작될 때, 필요한 모듈들을 로딩하여 메모리에 올립니다.
그 후, 클라이언트로부터 들어오는 모든 HTTP 요청(API, Page)을 각 모듈의 적절한 Controller나 Page로 전달하고 응답을 처리하는 중심 역할을 수행합니다.
ack 서버의 appsettings.json 에 로드되는 모듈 정보를 확인하세요.
잠깐, 구분해 보기
소스의 module.json을 바꿨는데 동작이 그대로인 이유는 무엇일까요?
로딩되는 모듈 확인하기 (2/3)
{
"LoadModules" : [
"wwwroot" ,
"transact" ,
"dbclient" ,
"graphclient" ,
"function" ,
"command" ,
"prompter" ,
"repository" ,
"logger" ,
"checkup" ,
"forwarder"
checkup 모듈 설정 확인하기 (2/3)
{
"ModuleID" : "checkup" ,
"Name" : "checkup" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"ManagedAccessKey" : "6eac215f2f5e495cad4f2abfdcad7644" ,
"EncryptionAES256Key" : "1234567890123456" ,
"AdministratorEmailID" : "administrator@handstack.kr" ,
"ModuleConfigurationUrl" : "http://localhost:8421/checkup/api/managed/initialize-settings" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"ModuleLogFilePath" : "../log/checkup/module.log" ,
checkup 모듈 설정 확인하기 (3/3)
"ModuleBasePath" : "../modules/checkup" ,
"WWWRootBasePath" : "../modules/checkup/wwwroot/checkup" ,
"EventAction" : [
"repository.Events.RepositoryRequest"
] ,
"SubscribeAction" : [ ] ,
"ConnectionString" : "URI=file:../sqlite/HDS/dbclient/checkup.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;"
}
}
dbclient 모듈 설정 확인하기 (1/8)
dbclient 모듈 참고하기
dbclient 모듈 설정 확인하기 (2/8)
{
"ModuleID" : "dbclient" ,
"Name" : "dbclient" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"CircuitBreakResetSecond" : 60 ,
"DefaultCommandTimeout" : 30 ,
"ContractBasePath" : [
"../contracts/dbclient"
] ,
"IsTransactionLogging" : false ,
dbclient 모듈 설정 확인하기 (3/8)
"ModuleLogFilePath" : "../log/dbclient/module.log" ,
"IsLogServer" : true ,
"LogServerUrl" : "http://localhost:8421/logger/api/log/insert" ,
"IsProfileLogging" : false ,
"ProfileLogFilePath" : "../log/dbclient/profile.log" ,
"EventAction" : [ ] ,
"SubscribeAction" : [
"dbclient.Events.DbClientRequest" ,
"dbclient.Events.ManagedRequest"
] ,
"DataSource" : [
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
dbclient 모듈 설정 확인하기 (4/8)
"DataSourceID" : "CHECKUPDB" ,
"DataProvider" : "SQLite" ,
"ConnectionString" : "URI=file:../sqlite/HDS/dbclient/checkup.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;" ,
"IsEncryption" : "N" ,
"Comment" : "SQLite 기본 데이터베이스"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "DB01" ,
"DataProvider" : "SQLite" ,
"ConnectionString" : "URI=file:../sqlite/HDS/dbclient/HDS.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;" ,
dbclient 모듈 설정 확인하기 (5/8)
"IsEncryption" : "N" ,
"Comment" : "SQLite 기본 데이터베이스"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "DB02" ,
"DataProvider" : "SqlServer" ,
"ConnectionString" : "Data Source=localhost;Initial Catalog=master;User ID=sa;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"Comment" : "SqlServer 기본 데이터베이스"
} ,
{
dbclient 모듈 설정 확인하기 (6/8)
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "DB03" ,
"DataProvider" : "Oracle" ,
"ConnectionString" : "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SID=ORCL)));User Id=system;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"Comment" : "Oracle 기본 데이터베이스"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "DB04" ,
"DataProvider" : "MySQL" ,
dbclient 모듈 설정 확인하기 (7/8)
"ConnectionString" : "Server=localhost;Port=3306;Uid=root;Pwd=Strong@Passw0rd;PersistSecurityInfo=True;SslMode=none;Charset=utf8;Allow User Variables=True;" ,
"IsEncryption" : "N" ,
"Comment" : "MySQL 기본 데이터베이스"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "DB05" ,
"DataProvider" : "PostgreSQL" ,
"ConnectionString" : "Host=localhost;Port=5432;Database=postgres;User ID=postgres;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"Comment" : "PostgreSQL 기본 데이터베이스"
dbclient 모듈 설정 확인하기 (8/8)
}
]
}
}
function 모듈 설정 확인하기 (1/8)
nuget, npm, pypi 패키지 관리자로 서버 함수에 필요한 패키지를 관리합니다.
function 모듈 참고하기
function 모듈 설정 확인하기 (2/8)
{
"ModuleID" : "function" ,
"Name" : "function" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"CircuitBreakResetSecond" : 60 ,
"IsLogServer" : true ,
"LogServerUrl" : "http://localhost:8421/logger/api/log/insert" ,
"ContractBasePath" : [
"../contracts/function"
] ,
function 모듈 설정 확인하기 (3/8)
"ModuleLogFilePath" : "../log/function/module.log" ,
"NodeFunctionConfig" : {
"LocalStoragePath" : "../cache/function" ,
"LogMinimumLevel" : "trace" ,
"FileLogBasePath" : "../log/function/javascript" ,
"TimeoutMS" : -1 ,
"IsSingleThread" : true ,
"WatchGracefulShutdown" : true ,
"EnableFileWatching" : true ,
"WatchFileNamePatterns" : [ "featureMain.js" ] ,
"NodeAndV8Options" : "" ,
"EnvironmentVariables" : ""
} ,
"CSharpFunctionConfig" : {
function 모듈 설정 확인하기 (4/8)
"EnableFileWatching" : true ,
"FileLogBasePath" : "../log/function/csharp" ,
"WatchFileNamePatterns" : [ "featureMain.cs" ] ,
} ,
"EventAction" : [ ] ,
"SubscribeAction" : [ ] ,
"FunctionSource" : [
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "FN01" ,
"DataProvider" : "SQLite" ,
"ConnectionString" : "URI=file:../sqlite/HDS/dbclient/HDS.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;" ,
function 모듈 설정 확인하기 (5/8)
"IsEncryption" : "N" ,
"WorkingDirectoryPath" : "../tmp/HDS/function/HDS_FN01" ,
"Comment" : "SQLite 기본 거래"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "FN02" ,
"DataProvider" : "SqlServer" ,
"ConnectionString" : "Data Source=localhost;Initial Catalog=master;User ID=sa;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"WorkingDirectoryPath" : "../tmp/HDS/function/HDS_FN02" ,
"Comment" : "SqlServer 기본 거래"
function 모듈 설정 확인하기 (6/8)
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "FN03" ,
"DataProvider" : "Oracle" ,
"ConnectionString" : "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SID=ORCL)));User Id=system;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"WorkingDirectoryPath" : "../tmp/HDS/function/HDS_FN03" ,
"Comment" : "Oracle 기본 거래"
} ,
{
"ApplicationID" : "HDS" ,
function 모듈 설정 확인하기 (7/8)
"ProjectID" : "*" ,
"DataSourceID" : "FN04" ,
"DataProvider" : "MySQL" ,
"ConnectionString" : "Server=localhost;Port=3306;Uid=root;Pwd=Strong@Passw0rd;PersistSecurityInfo=True;SslMode=none;Charset=utf8;Allow User Variables=True;" ,
"IsEncryption" : "N" ,
"WorkingDirectoryPath" : "../tmp/HDS/function/HDS_FN04" ,
"Comment" : "MySQL 기본 거래"
} ,
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"DataSourceID" : "FN05" ,
"DataProvider" : "PostgreSQL" ,
function 모듈 설정 확인하기 (8/8)
"ConnectionString" : "Host=localhost;Port=5432;Database=postgres;User ID=postgres;Password=Strong@Passw0rd;" ,
"IsEncryption" : "N" ,
"WorkingDirectoryPath" : "../tmp/HDS/function/HDS_FN05" ,
"Comment" : "PostgreSQL 기본 거래"
}
]
}
}
repository 모듈 설정 확인하기 (1/3)
repository 모듈 참고하기
repository 모듈 설정 확인하기 (2/3)
{
"ModuleID" : "repository" ,
"Name" : "repository" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"FileServerUrl" : "http://localhost:8421" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"ContractBasePath" : [
"../contracts/repository"
] ,
"ModuleLogFilePath" : "../log/repository/module.log" ,
"DatabaseContractPath" : "../contracts/dbclient" ,
repository 모듈 설정 확인하기 (3/3)
"ModuleBasePath" : "../modules/repository" ,
"EventAction" : [ ] ,
"SubscribeAction" : [ "repository.Events.RepositoryRequest" ] ,
"XFrameOptions" : "ALLOW-FROM http://127.0.0.1:8421,http://localhost:8421" ,
"ContentSecurityPolicy" : "frame-ancestors 'self' http://127.0.0.1:8421 http://localhost:8421;"
}
}
transact 모듈 설정 확인하기 (1/6)
transact 모듈 참고하기
transact 모듈 설정 확인하기 (2/6)
{
"ModuleID" : "transact" ,
"Name" : "transact" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"CircuitBreakResetSecond" : 60 ,
"IsValidationRequest" : false ,
"IsAllowDynamicRequest" : false ,
"AllowTenantTransactionCommands" : [ "D" , "G" ] ,
"IsLogServer" : true ,
"LogServerUrl" : "http://localhost:8421/logger/api/log/insert" ,
transact 모듈 설정 확인하기 (3/6)
"IsTransactionLogging" : true ,
"IsTransactAggregate" : true ,
"IsDataMasking" : false ,
"MaskingChar" : "*" ,
"MaskingMethod" : "Syn" ,
"ContractBasePath" : [
"../contracts/transact"
] ,
"AvailableEnvironment" : [ "P" , "D" , "S" ] ,
"IsCodeDataCache" : true ,
"CodeDataCacheTimeout" : 20 ,
"ModuleBasePath" : "../modules/transact" ,
"TransactionAggregateBasePath" : "../sqlite/HDS/transact/aggregate" ,
"UseApiAuthorize" : false ,
transact 모듈 설정 확인하기 (4/6)
"BypassAuthorizeIP" : [
"localhost" ,
"127.0.0.1"
] ,
"AllowRequestTransactions" : {
"HDS" : [ "*" ]
} ,
"RoutingCommandUri" : {
"HDS|*|D|D" : "http://localhost:8421/dbclient/api/query" ,
"HDS|*|G|D" : "http://localhost:8421/graphclient/api/query" ,
"HDS|*|F|D" : "http://localhost:8421/function/api/execution" ,
"HDS|*|C|D" : "http://localhost:8421/command/api/execution" ,
"HDS|*|P|D" : "http://localhost:8421/prompter/api/query" ,
"HDS|*|D|P" : "http://localhost:8421/dbclient/api/query" ,
transact 모듈 설정 확인하기 (5/6)
"HDS|*|G|P" : "http://localhost:8421/graphclient/api/query" ,
"HDS|*|F|P" : "http://localhost:8421/function/api/execution" ,
"HDS|*|C|P" : "http://localhost:8421/command/api/execution" ,
"HDS|*|P|P" : "http://localhost:8421/prompter/api/query" ,
"HDS|*|D|T" : "http://localhost:8421/dbclient/api/query" ,
"HDS|*|G|T" : "http://localhost:8421/graphclient/api/query" ,
"HDS|*|F|T" : "http://localhost:8421/function/api/execution" ,
"HDS|*|C|T" : "http://localhost:8421/command/api/execution" ,
"HDS|*|P|T" : "http://localhost:8421/prompter/api/query"
} ,
"EventAction" : [ ] ,
"SubscribeAction" : [
"transact.Events.TransactRequest"
] ,
transact 모듈 설정 확인하기 (6/6)
"PublicTransactions" : [
{
"ApplicationID" : "HDS" ,
"ProjectID" : "*" ,
"TransactionID" : "*"
}
]
}
}
logger 모듈 설정 확인하기 (1/3)
logger 모듈 참고하기
logger 모듈 설정 확인하기 (2/3)
{
"ModuleID" : "logger" ,
"Name" : "logger" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"IsSQLiteCreateOnNotSettingRequest" : true ,
"LogDeleteRepeatSecond" : 43200 ,
"ModuleBasePath" : "../modules/logger" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"EventAction" : [ ] ,
"SubscribeAction" : [ ] ,
"DataSource" : [
logger 모듈 설정 확인하기 (3/3)
{
"ApplicationID" : "HDS" ,
"TableName" : "TransactLog" ,
"DataProvider" : "SQLite" ,
"RemovePeriod" : -30 ,
"ConnectionString" : "URI=file:../sqlite/HDS/logger/transact.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;" ,
"IsEncryption" : "N"
}
]
}
}
wwwroot 모듈 설정 확인하기 (1/2)
wwwroot 모듈 참고하기
libman.json 으로 화면 개발에 필요한 오픈소스 관리합니다.
wwwroot 모듈 설정 확인하기 (2/2)
{
"ModuleID" : "wwwroot" ,
"Name" : "wwwroot" ,
"IsBundledWithHost" : false ,
"Version" : "1.0.0" ,
"ModuleConfig" : {
"SystemID" : "HANDSTACK" ,
"BusinessServerUrl" : "http://localhost:8421/transact/api/transaction/execute" ,
"ModuleLogFilePath" : "../log/wwwroot/module.log" ,
"ContractRequestPath" : "view" ,
"ContractBasePath" : "../contracts/wwwroot" ,
"WWWRootBasePath" : "../modules/wwwroot/wwwroot"
}
}
새로 추가된 기본 모듈 확인하기
현재 HandStack 기본 모듈에는 다음 확장 모듈도 포함됩니다.
모듈
역할
command
계약 기반 CLI 프로세스와 Web URL 호출 실행
graphclient
Neo4j, Memgraph 기반 Cypher 실행
prompter
LLM 프롬프트 계약 실행과 도구 호출 관리
forwarder
Playwright 기반 세션 유지형 포워드 프록시
자세한 설정은 /docs/reference/api/modules/[모듈 ID] 문서를 참고하세요.
파일에서 실행까지
요청을 받을 Controller와 화면을 렌더링할 Pages를 찾습니다.
계약 경로와 LoadModules를 대조합니다.
모듈별 설정은 필요한 키부터 읽고 전체 예제와 참조 문서로 확장합니다.
설정 예제의 키·계정·비밀번호는 교육용 값입니다. 운영에서는 환경별 비밀 값으로 교체하고 로그·소스에 노출하지 않습니다. 전체 JSON은 연속된 참고 페이지로 제공하며 발표에서는 변경할 필드부터 확인합니다.
발표: 첫 화면의 목표를 말한 뒤 핵심 개념과 예제로 진행합니다. 확인 질문 뒤에는 답할 시간을 주고, 마지막 완료 기준을 남겨 질문을 받습니다.
발표 구성 참고: MIT OpenCourseWare, Patrick Winston, How to Speak (2018), https://ocw.mit.edu/courses/res-tll-005-how-to-speak-january-iap-2018/pages/how-to-speak/
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
질문 후 잠시 기다립니다. 답이 없으면 앞에서 본 예제를 다시 가리킵니다.
확인할 답: 로드 모듈 목록과 실행 폴더의 설정·계약 경로를 확인합니다. 소스와 실행본이 다를 수 있습니다.
다음 주제로 넘어가기 전에 차이를 청중의 표현으로 한 번 확인합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.
질문을 받는 동안 이 확인 기준을 화면에 남깁니다. 청중이 자신의 업무에 적용할 다음 행동 하나를 고르게 합니다.